package com.lanyou.esb.cook.proxy.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import redis.clients.jedis.exceptions.JedisException;

import com.lanyou.esb.cook.proxy.common.Constants4Redis;
import com.lanyou.esb.cook.proxy.common.DateUtils;
import com.lanyou.esb.cook.proxy.dao.impl.BaseDao;
import com.lanyou.esb.cook.proxy.entity.AccLog;
import com.lanyou.esb.cook.proxy.entity.FieldSystem;
import com.lanyou.esb.cook.proxy.entity.Http;
import com.lanyou.esb.cook.proxy.entity.IfAlarm;
import com.lanyou.esb.cook.proxy.entity.IfAlarmWorkOrder;
import com.lanyou.esb.cook.proxy.redis.CommonUtils;
import com.lanyou.esb.cook.proxy.redis.proxy.impl.IfAlarmJedisProxy;
import com.lanyou.esb.cook.proxy.redis.proxy.impl.IfAlarmRepeatJedisProxy;
import com.lanyou.esb.cook.proxy.redis.proxy.impl.IfAlarmRepeatTimesJedisProxy;
import com.lanyou.esb.cook.proxy.redis.proxy.impl.IfAlarmUpdateJedisProxy;
import com.lanyou.esb.cook.proxy.redis.vo.Group;
import com.lanyou.esb.cook.proxy.redis.vo.Group.Entry;
import com.lanyou.esb.cook.proxy.redis.vo.RedisActionType;
import com.lanyou.esb.cook.proxy.service.GroupService;
import com.lanyou.esb.cook.proxy.service.IfAlarmService;
import com.lanyou.esb.cook.proxy.service.IfAlarmWorkOrderService;
import com.lanyou.esb.cook.proxy.service.RedisBatchService;
import com.lanyou.esb.cook.proxy.service.Service;
import com.lanyou.esb.cook.proxy.service.push.Push;

/**
 * 接口告警信息
 * 
 * @author Davey.wu
 */
public class IfAlarmServiceImpl implements IfAlarmService,
		GroupService<IfAlarm>, Service<IfAlarm> {
	/** 接口告警锁 **/
	private static String LOCK = "IFALARM";
	/** 数据库操作接口 **/
	private BaseDao<IfAlarm> dao;

	/** Redis代理接口 **/
	private IfAlarmJedisProxy jedisProxy;

	/** 接口告警重复Redis代理接口 **/
	private IfAlarmRepeatJedisProxy repeatJedisProxy;

	/** 接口更新重复Redis代理接口 **/
	private IfAlarmUpdateJedisProxy updateJedisProxy;

	/** 接口告警工单处理接口 **/
	private IfAlarmWorkOrderService ifAlarmWorkOrderService;

	/** 接口告警重复次数Redis代理接口 **/
	private IfAlarmRepeatTimesJedisProxy ifAlarmRepeatTimesJedisProxy;

	/** Redis批处理接口 **/
	private RedisBatchService redisBatchService;

	/** 消息推送接口 **/
	private Push<IfAlarm> push;

	@Override
	public Group getGroup(IfAlarm alarm) {
		if (alarm == null) {
			return null;
		}
		String json = "";
		try {
			json = CommonUtils.toJson(alarm);
		} catch (Exception e) {
			// 对象不能转换成JSON格式，抛弃该对象
			return null;
		}
		Group group = new Group();
		group.setLock(LOCK);
		// list类型条件集合
		List<Entry> lists = new ArrayList<>(2);
		// string类型条件集合
		List<Entry> strs = new ArrayList<>(2);
		group.setLists(lists);
		group.setStrs(strs);
		Entry entry = null;
		// 重复告警键
		String repeatKey = repeatJedisProxy.getKeyFromObject(alarm);
		// 不是重复告警
		if (!alarm.isRepeat()) {
			// 添加告警
			entry = new Entry();
			entry.setKey(jedisProxy.getKey());
			entry.setValue(json);
			// 保存到REDIS时，需要过滤，避免生成多条重复告警
			entry.setFilter(repeatKey);
			lists.add(entry);
		} else {
			// 添加告警
			entry = new Entry();
			entry.setKey(updateJedisProxy.getKey());
			entry.setValue(repeatKey);
			lists.add(entry);
		}
		// 初始化重复次数
		entry = new Entry();
		entry.setKey(ifAlarmRepeatTimesJedisProxy.getKey(alarm.getId()));
		entry.setValue(1 + "");
		entry.setTimeOut(ifAlarmRepeatTimesJedisProxy.getTimeOut());
		entry.setType(RedisActionType.INCR_BY);
		strs.add(entry);

		// 保存重复告警，用来判断之后的告警是否是重复告警
		entry = new Entry();
		entry.setKey(repeatJedisProxy.getKeyFromObject(alarm));
		entry.setValue(json);
		entry.setTimeOut(repeatJedisProxy.getTimeOut());
		entry.setType(RedisActionType.SET);
		strs.add(entry);
		return group;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public void save(final List<IfAlarm> alarms) throws Exception {
		if (alarms == null || alarms.isEmpty()) {
			return;
		}

		try {
			// 工单集合
			final List<IfAlarmWorkOrder> orders = new ArrayList<>(alarms.size());
			IfAlarmWorkOrder order = null;
			// 生成工单
			for (IfAlarm alarm : alarms) {
				order = alarm.getOrder();
				// 根据告警信息生成工单
				if (order == null) {
					order = ifAlarmWorkOrderService.generateWorkOrder(alarm);
					// 根据工单编码关联工单
					alarm.setAssignWorkCode(order.getCode());
					alarm.setOrder(order);
				}
				orders.add(order);
			}
			// 对告警信息及工单的保存使用事务管理，告警和工单必须同时保存
			try {
				TransactionTemplate tt = new TransactionTemplate(
						dao.getTransactionManager());
				tt.execute(new TransactionCallback() {
					public Object doInTransaction(TransactionStatus status) {
						// 保存告警信息
						IfAlarmServiceImpl.this.getDao().saveAll(alarms);
						// 保存工单
						ifAlarmWorkOrderService.save(orders);
						return status;
					}
				});
			} catch (Exception e) {
				throw e;
			}
		} catch (Exception e) {
			// 保存失败，将告警信息加入消息队列，用定时任务去尝试推送到DB
			push.putToCollection4DB(alarms);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public void update(final List<IfAlarm> alarms) throws Exception {
		if (alarms == null || alarms.isEmpty()) {
			return;
		}
		try {
			try {
				TransactionTemplate tt = new TransactionTemplate(
						dao.getTransactionManager());
				tt.execute(new TransactionCallback() {
					public Object doInTransaction(TransactionStatus status) {
						// 保存告警信息
						dao.update(alarms);
						return status;
					}
				});
			} catch (Exception e) {
				throw e;
			}
		} catch (Exception e) {
			// 保存失败，将告警信息加入消息队列，用定时任务去尝试推送到DB
			push.putToCollection4DB4Update(alarms);
		}
	}

	/**
	 * 将接口告警信息存储到Redis
	 *
	 * @param alarm
	 */
	@Override
	public void pushToRedis(IfAlarm alarm) {
		if (alarm == null) {
			return;
		}
		try {
			IfAlarm repeat = getRepeat(alarm);
			// 非重复告警，生成工单
			if (repeat == null) {
				// 生成工单
				IfAlarmWorkOrder order = ifAlarmWorkOrderService
						.generateWorkOrder(alarm);
				// 设置告警的关联工单
				alarm.setAssignWorkCode(order.getCode());
				alarm.setOrder(order);
			} else {
				// 设置重复标记
				alarm.setRepeat(true);
				// 设置告警标识
				alarm.setId(repeat.getId());
				// 重复告警，更新日期
				alarm.setLastUpdatedDate(new Date());
			}
			Group group = getGroup(alarm);
			if (group == null) {
				return;
			}
			// 批量将消息推送至redis,加事务控制
			redisBatchService.batchPushTransaction(group);
		} catch (Exception e) {
			// 保存失败，将告警信息加入消息队列，用定时任务去尝试推送到REDIS
			push.putToCollection4Redis(alarm);
		}
	}

	/**
	 * 判断是否是连续重复告警 <br>
	 * 重复条件：<br>
	 * 1，目标系统编码相同；<br>
	 * 2，接口编码相同；<br>
	 * 3，接口版本相同；<br>
	 * 4，调用方系统编码相同；<br>
	 * 5，告警类型相同；<br>
	 * 6，告警等级相同；<br>
	 * 
	 * @param alarm
	 * @return
	 * @throws Exception
	 */
	@Override
	public IfAlarm getRepeat(IfAlarm alarm) throws JedisException {
		IfAlarm target = null;
		try {
			target = repeatJedisProxy.get(repeatJedisProxy
					.getKeyFromObject(alarm));
			// 不存在重复告警，返回空
			if (target == null) {
				return null;
			}
			// 存在重复告警，但已过期，返回空
			if (!DateUtils.after(target.getLastUpdatedDate(),
					Constants4Redis.REDIS_TIMEOUT_IF_ALARM_REPEAT_INTERVAL,
					alarm.getCreateDate())) {
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			target = null;
			// 如果是JEDIS异常，则抛出
			if (e instanceof JedisException) {
				throw (JedisException) e;
			}
		}
		return target;
	}

	@Override
	public void pushToRedis(List<IfAlarm> alarms, AccLog log) {
		if (alarms == null || alarms.isEmpty()) {
			return;
		}
		for (IfAlarm alarm : alarms) {
			alarm.setCreateDate(log.getCreatedDate());
			alarm.setCreator(log.getCreator());
			alarm.setIfId(log.getIfId());
			alarm.setIfName(log.getIfName());
			alarm.setIfType(log.getIfType());
			alarm.setInEsbDate(log.getInEsbDate());
			alarm.setOutEsbDate(log.getOutEsbDate());
			alarm.setLastUpdatedDate(log.getLastUpdatedDate());
			alarm.setPlatReqDate(log.getPlatReqDate());
			alarm.setPlatResDate(log.getPlatResDate());
			// alarm.setSystemId(proxyLog.get);
		}
		for (IfAlarm alarm : alarms) {
			pushToRedis(alarm);
		}

	}

	@Override
	public IfAlarm create(FieldSystem pubSystem, FieldSystem callSystem,
			Http http, HttpServletRequest reqeust, String type, String level,
			int flow, int threshold, String reqIp, String content,
			String resCode, String emailReceiver, Date errorDate,
			float answerTime, float peakValue, Date createDate) {
		IfAlarm alarm = new IfAlarm();
		alarm.setAlarmType(type).setAlarmLevel(level).setFlow(flow)
				.setFowThreshold(threshold)
				.setErrorReceiver(pubSystem.getCreator())
				.setSystemId(pubSystem.getId())
				.setSystemCode(pubSystem.getCode()).setIfCode(http.getCode())
				.setIfVersion(http.getVersion())
				.setCallSystemCode(callSystem.getCode()).setReqMess(content)
				.setReqIp(reqIp).setReqMethod(reqeust.getMethod())
				.setReqUrl(reqeust.getRequestURL().toString())
				.setResCode(resCode).setEmailReceiver(emailReceiver)
				.setErrorDate(errorDate).setAnswerTime(answerTime)
				.setPeakValue(peakValue).setCreateDate(createDate);
		return alarm;

	}

	public IfAlarmJedisProxy getJedisProxy() {
		return jedisProxy;
	}

	public void setJedisProxy(IfAlarmJedisProxy jedisProxy) {
		this.jedisProxy = jedisProxy;
	}

	public IfAlarmWorkOrderService getIfAlarmWorkOrderService() {
		return ifAlarmWorkOrderService;
	}

	public void setIfAlarmWorkOrderService(
			IfAlarmWorkOrderService ifAlarmWorkOrderService) {
		this.ifAlarmWorkOrderService = ifAlarmWorkOrderService;
	}

	public IfAlarmRepeatJedisProxy getRepeatJedisProxy() {
		return repeatJedisProxy;
	}

	public void setRepeatJedisProxy(IfAlarmRepeatJedisProxy repeatJedisProxy) {
		this.repeatJedisProxy = repeatJedisProxy;
	}

	public IfAlarmUpdateJedisProxy getUpdateJedisProxy() {
		return updateJedisProxy;
	}

	public void setUpdateJedisProxy(IfAlarmUpdateJedisProxy updateJedisProxy) {
		this.updateJedisProxy = updateJedisProxy;
	}

	public IfAlarmRepeatTimesJedisProxy getIfAlarmRepeatTimesJedisProxy() {
		return ifAlarmRepeatTimesJedisProxy;
	}

	public void setIfAlarmRepeatTimesJedisProxy(
			IfAlarmRepeatTimesJedisProxy ifAlarmRepeatTimesJedisProxy) {
		this.ifAlarmRepeatTimesJedisProxy = ifAlarmRepeatTimesJedisProxy;
	}

	public BaseDao<IfAlarm> getDao() {
		return dao;
	}

	public void setDao(BaseDao<IfAlarm> dao) {
		this.dao = dao;
	}

	public Push<IfAlarm> getPush() {
		return push;
	}

	public void setPush(Push<IfAlarm> push) {
		this.push = push;
	}

	public RedisBatchService getRedisBatchService() {
		return redisBatchService;
	}

	public void setRedisBatchService(RedisBatchService redisBatchService) {
		this.redisBatchService = redisBatchService;
	}
}
